package com.fastjars.business.upload;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.shiro.codec.Base64;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * ResourceUploader
 *
 * @author Brant Liu <br> 邮箱：<br>lbf1988@qq.com <br>日期：<br>2018/04/17
 * @version 1.0.0
 */
public class ResourceUploader {
    public static final int BUFFER_SIZE = 8192;

    public static ActionResult saveBase64(HttpServletRequest request, Map<String, Object> conf) {
        String fileName = request.getParameter(String.valueOf(conf.get("fieldName")));
        byte[] data = decode(fileName);

        long maxSize = Long.parseLong(String.valueOf(conf.get("maxSize")));
        if (!validSize(data, maxSize)) {
            return new ActionResult(ActionState.MAX_SIZE);
        }
        String suffix = ".jpg";
        String savePath = PathFormat.parse(String.valueOf(conf.get(ActionHandler.PARAM_SAVE_PATH)), String.valueOf(conf.get("filename")));
        savePath = savePath + suffix;
        String rootPath = ConfigManager.getRootPath(request,conf);
        String physicalPath = rootPath + savePath;
        ActionResult result = saveBinaryFile(data, physicalPath);
        if(ActionState.getStateInfo(ActionState.SUCCESS).equals(result.getState())){
            result.setUrl(PathFormat.format(savePath));
            result.setType(suffix);
            result.setOriginal("");
        }
        return result;
    }

    public static ActionResult saveBinary(HttpServletRequest request, Map<String, Object> conf) {
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
        if (!multipartResolver.isMultipart(request)) {
            // TODO: 2018/04/17 上传表单不是multipart/form-data类型
            return new ActionResult(ActionState.NOT_MULTIPART_CONTENT);
        }
//        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
//        if (RequestUtils.isAjaxRequest(request)) {
//            upload.setHeaderEncoding(Constant.APP_CHARSET_UTF8);
//        }
        try {
            // TODO: 2018/04/17 将request变成多部分request
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
            // TODO: 2018/04/17 获取multiRequest 中所有的文件名
            Iterator<String> iterator = multiRequest.getFileNames();

            MultipartFile multipartFile = null;
            while (iterator.hasNext()) {
                multipartFile = multiRequest.getFile(iterator.next().toString());
                if (multipartFile != null)
                    break;
            }
            if (null == multipartFile) {
                // TODO: 2018/04/17 未找到上传数据
                return new ActionResult(ActionState.NOT_FOUND_UPLOAD_DATA);
            }
            // TODO: 2018/04/17 获取保存路径
            String savePath = (String) conf.get(ActionHandler.PARAM_SAVE_PATH);
            // TODO: 2018/04/17 获取原来的名称
            String originFileName = multipartFile.getOriginalFilename();
            // TODO: 2018/04/17 获取文件后缀
            String suffix = FilenameUtils.getExtension(originFileName);

            originFileName = originFileName.substring(0, originFileName.length() - suffix.length());
            savePath = savePath + suffix;

            long maxSize = ((Long) conf.get("maxSize")).longValue();

            if (!validType(suffix, (String[]) conf.get("allowFiles"))) {
                return new ActionResult(ActionState.NOT_ALLOW_FILE_TYPE);
            }
            savePath = PathFormat.parse(savePath, originFileName);
            String physicalPath = String.valueOf(conf.get(ActionHandler.PARAM_ROOT_PATH)) + savePath;
            ActionResult result = saveFileByInputStream(multipartFile.getInputStream(), physicalPath, maxSize);
            if (ActionState.getStateInfo(ActionState.SUCCESS).equals(result.getState())) {
                result.setUrl(PathFormat.format(savePath));
                result.setType(suffix);
                result.setOriginal(originFileName + suffix);
            }
            return result;
        } catch (IOException e) {
            //忽略
        }
        return new ActionResult(ActionState.IO_ERROR);
    }

    public static ActionResult saveBinaryFile(byte[] data, String path) {
        File file = new File(path);
        ActionResult state = valid(file);
        if (!ActionState.getStateInfo(ActionState.SUCCESS).equals(state.getState())) {
            return state;
        }
        try {
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
            bos.write(data);
            bos.flush();
            bos.close();
        } catch (IOException ioe) {
            return new ActionResult(ActionState.IO_ERROR);
        }
        state = new ActionResult(ActionState.SUCCESS);
        state.setSize(data.length + "");
        state.setName(file.getName());
        return state;
    }

    public static ActionResult saveFileByInputStream(InputStream is, String path, long maxSize) {
        ActionResult state = null;
        File tmpFile = getTmpFile();
        byte[] dataBuf = new byte[2048];
        BufferedInputStream bis = new BufferedInputStream(is, BUFFER_SIZE);
        try {
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tmpFile), BUFFER_SIZE);
            int count = 0;
            while ((count = bis.read(dataBuf)) != -1) {
                bos.write(dataBuf, 0, count);
            }
            bos.flush();
            bos.close();
            if (tmpFile.length() > maxSize) {
                tmpFile.delete();
                return new ActionResult(ActionState.MAX_SIZE);
            }
            state = saveTmpFile(tmpFile, path);
            if (!ActionState.getStateInfo(ActionState.SUCCESS).equals(state.getState())) {
                tmpFile.delete();
            }
            return state;
        } catch (IOException e) {
        }
        return new ActionResult(ActionState.IO_ERROR);
    }

    public static ActionResult saveFileByInputStream(InputStream is, String path) {
        ActionResult state = null;
        File tmpFile = getTmpFile();
        byte[] dataBuf = new byte[2048];
        BufferedInputStream bis = new BufferedInputStream(is, BUFFER_SIZE);
        try {
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tmpFile), BUFFER_SIZE);
            int count = 0;
            while ((count = bis.read(dataBuf)) != -1) {
                bos.write(dataBuf, 0, count);
            }
            bos.flush();
            bos.close();
            state = saveTmpFile(tmpFile, path);
            if (!ActionState.getStateInfo(ActionState.SUCCESS).equals(state.getState())) {
                tmpFile.delete();
            }
            return state;
        } catch (IOException e) {
        }
        return new ActionResult(ActionState.IO_ERROR);
    }

    private static byte[] decode(String content) {
        return Base64.decode(content.getBytes());
    }

    private static boolean validSize(byte[] data, long length) {
        return data.length <= length;
    }

    private static boolean validType(String type, String[] allowTypes) {
        List<String> list = Arrays.asList(allowTypes);
        return list.contains(type);
    }

    private static File getTmpFile() {
        File tmpDir = FileUtils.getTempDirectory();
        String tmpFileName = (Math.random() * 10000 + "").replace(".", "");
        return new File(tmpDir, tmpFileName);
    }

    private static ActionResult saveTmpFile(File tmpFile, String path) {
        ActionResult state = null;
        File targetFile = new File(path);
        if (targetFile.canWrite()) {
            return new ActionResult(ActionState.PERMISSION_DENIED);
        }
        try {
            FileUtils.moveFile(tmpFile, targetFile);
        } catch (IOException e) {
            return new ActionResult(ActionState.IO_ERROR);
        }
        state = new ActionResult(ActionState.SUCCESS);
        state.setSize(targetFile.length() + "");
        state.setName(targetFile.getName());
        return state;
    }

    private static ActionResult valid(File file) {
        File parentPath = file.getParentFile();
        if ((!parentPath.exists()) && (!parentPath.mkdirs())) {
            return new ActionResult(ActionState.FAILED_CREATE_FILE);
        }
        if (!parentPath.canWrite()) {
            return new ActionResult(ActionState.PERMISSION_DENIED);
        }
        return new ActionResult(ActionState.SUCCESS);
    }
}
